package com.sean.community.quartz;

import com.sean.community.entity.DiscussPost;
import com.sean.community.event.EventProducer;
import com.sean.community.service.DiscussPostService;
import com.sean.community.service.ElasticsearchService;
import com.sean.community.service.LikeService;
import com.sean.community.util.CommunityConstant;
import com.sean.community.util.RedisKeyUtil;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundSetOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * 刷新帖子分数任务
 * 该任务设计并不完善，可能存在被刷分的情况，例如反复点赞会导致重新计算分数
 */
@Component
public class PostScoreRefreshJob implements Job, CommunityConstant {
    private static final Logger logger = LoggerFactory.getLogger(PostScoreRefreshJob.class);
    private RedisTemplate<String, Object> redisTemplate;    // 计算数据来源
    private DiscussPostService discussPostService;          // 用于更新帖子
    private LikeService likeService;                        // 用于查询点赞数量
    private ElasticsearchService elasticsearchService;      // 将帖子同步到 es 服务器
    private static Date epoch;                              // 论坛成立时间
    static {
        try {
            epoch = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse("2014-08-01 00:00:00");
        } catch (ParseException e) {
            throw new RuntimeException("初始化论坛纪元失败！");
        }
    }

    @Autowired
    public void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    @Autowired
    public void setDiscussPostService(DiscussPostService discussPostService) {
        this.discussPostService = discussPostService;
    }

    @Autowired
    public void setLikeService(LikeService likeService) {
        this.likeService = likeService;
    }

    @Autowired
    public void setElasticsearchService(ElasticsearchService elasticsearchService) {
        this.elasticsearchService = elasticsearchService;
    }

    @Override
    @SuppressWarnings("all")
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        String postScoreKey = RedisKeyUtil.getPostScoreKey();
        // 设置绑定到某个键的操作。
        // 批量操作
        BoundSetOperations<String, Object> operations = redisTemplate.boundSetOps(postScoreKey);
        if(operations.size() == 0){
            logger.info("任务取消，没有需要刷新分数的帖子");
            return;
        }
        logger.info("[任务开始] 正在刷新帖子分数：" + operations.size());
        while (operations.size() > 0){
            refreshDiscussPostScore((Integer) operations.pop());
        }
        logger.info("[任务结束] 帖子分数刷新完毕：" + operations.size());
    }

    private void refreshDiscussPostScore(int discussPostId){
        DiscussPost discussPost = discussPostService.findDiscussPostById(discussPostId);
        if(discussPost == null){
            logger.error("帖子不存在：" + discussPostId);
            return;
        }
        // 是否是精华帖
        boolean isRefinePost = discussPost.getStatus() == DISCUSSPOST_STATUS_REFINE;
        // 评论数量
        int commentCount = discussPost.getCommentCount();
        // 点赞数量
        long likeCount = likeService.findEntityLikeCount(ENTITY_TYPE_DISCUSS_POST, discussPostId);
        // 热帖分数计算公式 log(精华分 + 评论数 * 10 + 点赞数 * 2 + 收藏数 * 2) + （发布时间 - 论坛创建时间）（天）
        // 计算权重 log(w)
        double weight = (isRefinePost ? 75 : 0) + commentCount * 10 + likeCount * 2;
        // 计算分数
        double score = Math.log10(Math.max(weight, 1)) +
                ((discussPost.getCreateTime().getTime() - epoch.getTime()) / (86400.0 * 1000));
        // 更新帖子分数
        discussPostService.updateScoreById(discussPostId, score);
        // 同步 es
        discussPost.setScore(score);
        elasticsearchService.saveDiscussPost(discussPost);
    }
}
